package block

import (
	"bufio"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"os"
	"sync"
)

type FileBlockCache struct {
	sizeMB int
	mutex  *sync.Mutex // 互斥锁
	blocks [][]byte    // 分块空间
}

// New 实例
func New() *FileBlockCache {
	return &FileBlockCache{
		sizeMB: 1024 * 1024, // 默认1MB
		mutex:  &sync.Mutex{},
	}
}

// ReadBlocks 读取文件块列表
func (c *FileBlockCache) ReadBlocks(filePath string, blockSizeMb int) ([][]byte, error) {
	c.mutex.Lock()
	// 关闭锁
	defer c.mutex.Unlock()

	// 是否已经开始截取分组Block
	if len(c.blocks) > 0 {
		return c.blocks, nil
	}

	// 切块尺寸不能小于等于0
	if c.sizeMB <= 0 || blockSizeMb <= 0 {
		return nil, errors.New(`block size not lte zero`)
	}

	// 打开准备拆分的文件
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf(`open file failed %w`, err)
	}
	// 使用完成关闭
	defer func() { _ = file.Close() }()

	// 初始化一下
	var (
		reader    = bufio.NewReader(file)
		blockSize = blockSizeMb * c.sizeMB
	)

	// 迭代处理
	for {
		buffer := make([]byte, blockSize) // 每次循环都创建一个新的缓冲区,接收新截取的文件内容
		// 读取内容
		n, err := reader.Read(buffer)
		if err == io.EOF {
			// 截取最后的文件内容
			if n > 0 {
				c.blocks = append(c.blocks, buffer[:n])
			}
			break
		}
		// 其他错误
		if err != nil {
			return nil, fmt.Errorf(`read block content failed %w`, err)
		}

		// 刷新到块空间(注：后续可以考虑把内存缓存改成其他缓存，例如：存储到临时文件块，或中间件，避免后续文件太大截取分组消耗完系统内存)
		c.blocks = append(c.blocks, buffer[:n])
	}
	return c.blocks, nil
}

// ToBase64 读取文件块并转换为base64
func (c *FileBlockCache) ToBase64(filePath string, blockSizeMB int) ([]string, error) {
	blocks, err := c.ReadBlocks(filePath, blockSizeMB)
	if err != nil {
		return nil, err
	}
	baseBlocks := make([]string, 0)
	for _, block := range blocks {
		baseBlocks = append(baseBlocks, base64.StdEncoding.EncodeToString(block))
	}
	return baseBlocks, nil
}

// SetSizeMb 设置切块单位，默认1MB
func (c FileBlockCache) SetSizeMb(sizeMb int) {
	c.sizeMB = sizeMb
}

// GetBlocks 获取文件块空间
func (c *FileBlockCache) GetBlocks() [][]byte {
	return c.blocks
}
